<?php
namespace Illuminate\Pipeline;

use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract;

use Illuminate\Http\Request;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Support\Responsable;
use Closure;

use RuntimeException;

class Pipeline implements PipelineContract {
    protected $container;
    protected $passable; // 处理对象
    protected $pipes = []; // pipes 处理过程
    protected $method = 'handle'; // 处理方法

    public function __construct(Container $container = null) {
        $this->container = $container;
    }

    public function send($passable) { // 处理对象
        $this->passable = $passable;

        return $this;
    }
    public function through($pipes) { // pipes 处理过程
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();

        return $this;
    }
    public function via($method) { // 处理方法
        $this->method = $method;

        return $this;
    }

    public function then(Closure $destination) {
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );
// print_r($this->pipes);
        return $pipeline($this->passable);
    }

    protected function prepareDestination(Closure $destination) {
        return function ($passable) use ($destination) {
            return $destination($passable);
        };
    }
    protected function carry() {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                if ( is_callable($pipe) ) { // 可执行
                    return $pipe($passable, $stack);
                } elseif ( ! is_object($pipe) ) { // 字符串
                    list($name, $parameters) = $this->parsePipeString($pipe);

                    $pipe = $this->getContainer()->make($name);

                    $parameters = array_merge([$passable, $stack], $parameters);
                } else { // 对象
                    $parameters = [$passable, $stack];
                }

                $response = method_exists($pipe, $this->method)
                    ? $pipe->{$this->method}(...$parameters)
                    : $pipe(...$parameters);

                return $response instanceof Responsable
                    ? $response->toResponse($this->container->make(Request::class))
                    : $response;
            };
        };
    }

    protected function parsePipeString($pipe) { // function:parameter1,parameter2,parameter3
        list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []); // 分开 方法 参数
        if ( is_string($parameters) ) $parameters = explode(',', $parameters); // 分开参数

        return [$name, $parameters];
    }

    protected function getContainer() {
        if ( ! $this->container ) throw new RuntimeException('A container instance has not been passed to the Pipeline.');

        return $this->container;
    }
}
